/*****************************************************************************
 *  ENTROPY - emerging network to reduce orwellian potency yield
 *
 *  Copyright (C) 2002 Juergen Buchmueller <pullmoll@stop1984.com>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software Foundation,
 *  Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 *
 *	$Id: ek5.c,v 1.2 2005/07/12 23:12:29 pullmoll Exp $
 *****************************************************************************/
#include "ek5.h"
#ifdef	EK5_TEST
#define	xmalloc(size) malloc(size)
#define	xcalloc(num,size) calloc(num,size)
#define	xfree(ptr) if (NULL != ptr) { free(ptr); ptr = NULL; }
#else
#include "memalloc.h"
#endif
#include "logger.h"

/*****************************************************************************
 *	EK5 - a modified MD5 hash algorithm
 *****************************************************************************/

/*****************************************************************************
 * The following code was used to generate the constants T0_0 to T3_f (64)
 *
#include <math.h>
main()
{
	int i;
	for (i = 0; i < 80; ++i) {
		unsigned long v = (unsigned long)(4294967295.0 * fabs(tan(1.0+i)));
		printf("#define T%x_%x 0x%08lx\n", 1+i/16, i%16, v);
		if (15 == (i%16))
			printf("\n");
	}
	return 0;
}
 *
 *****************************************************************************/

#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))

static int xdigit(char ch)
{
	switch (ch) {
	case '0': case '1': case '2': case '3': case '4':
	case '5': case '6': case '7': case '8': case '9':
		return ch - '0';
	case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
		return ch - 'a' + 10;
	case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
		return ch - 'A' + 10;
	}
	return -1;
}

/*****************************************************************************
 *	EK5 last block padding
 *****************************************************************************/
static const uint8_t pad[64] = {
	0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
};

#define T1_0 0x8eb245ca
#define T1_1 0x2f5ec5be
#define T1_2 0x247dee24
#define T1_3 0x2866f9bd
#define T1_4 0x61696e70
#define T1_5 0x4a7f61ba
#define T1_6 0xdf173709
#define T1_7 0xccb9e3cb
#define T1_8 0x73caf584
#define T1_9 0xa5faf9a5
#define T1_a 0xf36aab76
#define T1_b 0xa2c7b75d
#define T1_c 0x76888d8e
#define T1_d 0x3e9e8a01
#define T1_e 0xdb22622d
#define T1_f 0x4cf63c0f

#define T2_0 0x7e714174
#define T2_1 0x2326fdce
#define T2_2 0x26ce914a
#define T2_3 0x3cb69461
#define T2_4 0x870a24bb
#define T2_5 0x02441a25
#define T2_6 0x96913350
#define T2_7 0x22889706
#define T2_8 0x222ec95f
#define T2_9 0x2dc2cafc
#define T2_a 0x461173c4
#define T2_b 0x480bc543
#define T2_c 0xe31bcb1c
#define T2_d 0x67c3c903
#define T2_e 0x7112f5f2
#define T2_f 0xa937b121

#define T3_0 0x5021bc9e
#define T3_1 0x9f9da0c5
#define T3_2 0x794bebe8
#define T3_3 0xc01edc74
#define T3_4 0xd73cc8f4
#define T3_5 0x4f707435
#define T3_6 0x9d537004
#define T3_7 0x1e01cc35
#define T3_8 0x2920cc22
#define T3_9 0x4a986747
#define T3_a 0x7f965007
#define T3_b 0x04884b90
#define T3_c 0x9ea9963c
#define T3_d 0x162c4de9
#define T3_e 0x1fe109e8
#define T3_f 0x333b89fb

#define T4_0 0x2c43bc1d
#define T4_1 0x459b474c
#define T4_2 0xe730a764
#define T4_3 0x0da3423f
#define T4_4 0x6e606231
#define T4_5 0xac7e29d3
#define T4_6 0x2eded93e
#define T4_7 0x9c7c6eb3
#define T4_8 0x7c153f9e
#define T4_9 0x54b308d8
#define T4_a 0xd365ecea
#define T4_b 0x51ee2abd
#define T4_c 0xbe40411c
#define T4_d 0x18f6669d
#define T4_e 0x2b74b841
#define T4_f 0x590d5f89

#define T5_0 0x78546d5f
#define T5_1 0x06ccab8b
#define T5_2 0xa6fe43a1
#define T5_3 0x0a42c99e
#define T5_4 0x1d9496e9
#define T5_5 0x38d25d7c
#define T5_6 0x13deee42
#define T5_7 0x432dc906
#define T5_8 0xeb565a91
#define T5_9 0xbcad8250
#define T5_a 0x6bb30eb7
#define T5_b 0xafceb251
#define T5_c 0x44c161ad
#define T5_d 0x9963dc40
#define T5_e 0x7ee4b909
#define T5_f 0x00ef87c6

static void ek5_process(ek5_state_t *pes, const uint8_t data[64])
{
	uint32_t a = pes->state[0];
	uint32_t b = pes->state[1];
	uint32_t c = pes->state[2];
	uint32_t d = pes->state[3];
	uint32_t t;
	uint32_t X[16];

#if	!WORDS_BIGENDIAN
	memcpy(X, data, sizeof(X));
#else
	const uint8_t *p = data;
	int i;
	for (i = 0; i < 16; i++, p += 4)
		X[i] = p[0] + (p[1] << 8) + (p[2] << 16) + (p[3] << 24);
#endif

/*
 * 1st round:
 * [abcd k s i] denote the operation
 * a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s)
 */

#define F(x, y, z) (((x) & (y)) | (~(x) & (z)))
#define EK5_R1(a, b, c, d, k, s, T) \
	t = a + F(b,c,d) + X[k] + T;\
	a = b + rol(t, s)

	EK5_R1(a,b,c,d, 0, 7,T1_0);
	EK5_R1(d,a,b,c, 1,12,T1_1);
	EK5_R1(c,d,a,b, 2,17,T1_2);
	EK5_R1(b,c,d,a, 3,22,T1_3);

	EK5_R1(a,b,c,d, 4, 7,T1_4);
	EK5_R1(d,a,b,c, 5,12,T1_5);
	EK5_R1(c,d,a,b, 6,17,T1_6);
	EK5_R1(b,c,d,a, 7,22,T1_7);

	EK5_R1(a,b,c,d, 8, 7,T1_8);
	EK5_R1(d,a,b,c, 9,12,T1_9);
	EK5_R1(c,d,a,b,10,17,T1_a);
	EK5_R1(b,c,d,a,11,22,T1_b);

	EK5_R1(a,b,c,d,12, 7,T1_c);
	EK5_R1(d,a,b,c,13,12,T1_d);
	EK5_R1(c,d,a,b,14,17,T1_e);
	EK5_R1(b,c,d,a,15,22,T1_f);

/*
 * 2nd round:
 * [abcd k s i] denote the operation
 * a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s)
 */

#define G(x,y,z) (((x) & (z)) | ((y) & ~(z)))
#define EK5_R2(a,b,c,d,k,s,T) \
	t = a + G(b,c,d) + X[k] + T;\
	a = b + rol(t,s)

	EK5_R2(a,b,c,d, 1, 5,T2_0);
	EK5_R2(d,a,b,c, 6, 9,T2_1);
	EK5_R2(c,d,a,b,11,14,T2_2);
	EK5_R2(b,c,d,a, 0,20,T2_3);

	EK5_R2(a,b,c,d, 5, 5,T2_4);
	EK5_R2(d,a,b,c,10, 9,T2_5);
	EK5_R2(c,d,a,b,15,14,T2_6);
	EK5_R2(b,c,d,a, 4,20,T2_7);

	EK5_R2(a,b,c,d, 9, 5,T2_8);
	EK5_R2(d,a,b,c,14, 9,T2_9);
	EK5_R2(c,d,a,b, 3,14,T2_a);
	EK5_R2(b,c,d,a, 8,20,T2_b);

	EK5_R2(a,b,c,d,13, 5,T2_c);
	EK5_R2(d,a,b,c, 2, 9,T2_d);
	EK5_R2(c,d,a,b, 7,14,T2_e);
	EK5_R2(b,c,d,a,12,20,T2_f);

/*
 * 3rd round:
 * [abcd k s i] denote the operation
 * a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s)
 */

#define H(x,y,z) ((x) ^ (y) ^ (z))
#define EK5_R3(a,b,c,d,k,s,T) \
	t = a + H(b,c,d) + X[k] + T; \
	a = b + rol(t,s)

	EK5_R3(a,b,c,d, 5, 4,T3_0);
	EK5_R3(d,a,b,c, 8,11,T3_1);
	EK5_R3(c,d,a,b,11,16,T3_2);
	EK5_R3(b,c,d,a,14,23,T3_3);

	EK5_R3(a,b,c,d, 1, 4,T3_4);
	EK5_R3(d,a,b,c, 4,11,T3_5);
	EK5_R3(c,d,a,b, 7,16,T3_6);
	EK5_R3(b,c,d,a,10,23,T3_7);

	EK5_R3(a,b,c,d,13, 4,T3_8);
	EK5_R3(d,a,b,c, 0,11,T3_9);
	EK5_R3(c,d,a,b, 3,16,T3_a);
	EK5_R3(b,c,d,a, 6,23,T3_b);

	EK5_R3(a,b,c,d, 9, 4,T3_c);
	EK5_R3(d,a,b,c,12,11,T3_d);
	EK5_R3(c,d,a,b,15,16,T3_e);
	EK5_R3(b,c,d,a, 2,23,T3_f);

/*
 * 4th round:
 * [abcd k s i] denote the operation
 * a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s)
 */

#define I(x,y,z) ((y) ^ ((x) | ~(z)))
#define EK5_R4(a,b,c,d,k,s,T) \
	t = a + I(b,c,d) + X[k] + T; \
	a = b + rol(t,s)

	EK5_R4(a,b,c,d, 0, 6,T4_0);
	EK5_R4(d,a,b,c, 7,10,T4_1);
	EK5_R4(c,d,a,b,14,15,T4_2);
	EK5_R4(b,c,d,a, 5,21,T4_3);

	EK5_R4(a,b,c,d,12, 6,T4_4);
	EK5_R4(d,a,b,c, 3,10,T4_5);
	EK5_R4(c,d,a,b,10,15,T4_6);
	EK5_R4(b,c,d,a, 1,21,T4_7);

	EK5_R4(a,b,c,d, 8, 6,T4_8);
	EK5_R4(d,a,b,c,15,10,T4_9);
	EK5_R4(c,d,a,b, 6,15,T4_a);
	EK5_R4(b,c,d,a,13,21,T4_b);

	EK5_R4(a,b,c,d, 4, 6,T4_c);
	EK5_R4(d,a,b,c,11,10,T4_d);
	EK5_R4(c,d,a,b, 2,15,T4_e);
	EK5_R4(b,c,d,a, 9,21,T4_f);

/*
 * 5th round:
 * [abcd k s i] denote the operation
 * a = b + ((a + J(b,c,d) + X[k] + T[i]) <<< s)
 */

#define J(x,y,z) (~(y) | (~(x) ^ (z)))
#define EK5_R5(a,b,c,d,k,s,T) \
	t = a + J(b,c,d) + X[k] + T; \
	a = b + rol(t,s)

	EK5_R4(a,b,c,d, 1, 2,T5_0);
	EK5_R4(d,a,b,c, 2,15,T5_1);
	EK5_R4(c,d,a,b, 3, 3,T5_2);
	EK5_R4(b,c,d,a, 4,16,T5_3);

	EK5_R4(a,b,c,d, 5, 2,T5_4);
	EK5_R4(d,a,b,c,10,15,T5_5);
	EK5_R4(c,d,a,b,15, 3,T5_6);
	EK5_R4(b,c,d,a, 0,16,T5_7);

	EK5_R4(a,b,c,d, 3, 2,T5_8);
	EK5_R4(d,a,b,c, 8,15,T5_9);
	EK5_R4(c,d,a,b,13, 3,T5_a);
	EK5_R4(b,c,d,a,12,16,T5_b);

	EK5_R4(a,b,c,d,11, 2,T5_c);
	EK5_R4(d,a,b,c, 6,15,T5_d);
	EK5_R4(c,d,a,b, 7, 3,T5_e);
	EK5_R4(b,c,d,a, 9,16,T5_f);

	/* Add the working vars back into context.state[] */
	pes->state[0] += a;
	pes->state[1] += b;
	pes->state[2] += c;
	pes->state[3] += d;

	memset(X, 0, sizeof(X));
	a = b = c = d = t = 0;
}

void ek5_init(ek5_state_t *pes)
{
	pes->state[0] = 0x33ccf00f;
	pes->state[1] = 0x08192a3b;
	pes->state[2] = 0xc4d5e6f7;
	pes->state[3] = 0xaa559966;

	pes->count[0] = 0;
	pes->count[1] = 0;
}

void ek5_append(ek5_state_t *pes, const void *src, size_t len)
{
	const uint8_t *p = (uint8_t *)src;
	int left = len;
	size_t offs = (pes->count[0] / 8) & 63;
	size_t nbits = (size_t)(len * 8);

	if (len <= 0)
		return;

	/* update the message length */
	pes->count[1] += len >> 29;
	pes->count[0] += nbits;
	if (pes->count[0] < nbits)
		pes->count[1]++;

	/* process an initial partial block */
	if (offs > 0) {
		int copy = (offs + len > 64 ? 64 - offs : len);

		memcpy(pes->accu + offs, p, copy);
		if (offs + copy < 64)
		    return;
		p += copy;
		left -= copy;
		ek5_process(pes, pes->accu);
	}

	/* process full blocks */
	for (/* */; left >= 64; p += 64, left -= 64)
		ek5_process(pes, p);

	/* process a final partial block */
	if (left > 0)
		memcpy(pes->accu, p, left);
}

void ek5_finish(ek5_state_t *pes, ek5_digest_t *pd)
{
	uint8_t finalcount[8];
	size_t i;

	/* save the length before padding */
	for (i = 0; i < 8; i++)
		finalcount[i] = (uint8_t)(pes->count[(i/4)] >> ((i&3) * 8));

	/* pad to 56 bytes mod 64 */
	ek5_append(pes, pad, ((55 - (pes->count[0] / 8)) & 63) + 1);

	/* Append the length. */
	ek5_append(pes, finalcount, 8);

	if (NULL != pd) {
		for (i = 0; i < 16; i++)
			pd->digest[i] = (uint8_t)(pes->state[i/4] >> ((i%4)*8));
	}
	memset(pes, 0, sizeof(*pes));
}

void ek5(const void *src, size_t len, ek5_digest_t *pd)
{
	ek5_state_t es;
	ek5_init(&es);
	ek5_append(&es, src, len);
	ek5_finish(&es, pd);
}

void ek5_from_sha1(const uint8_t sha1[20], ek5_digest_t *pd)
{
	size_t i, j;
	int c;

	for (i = 0; i < EK5SIZE; i++)
		pd->digest[i] = sha1[i];
	for (j = 0; j < 4; j++) {
		c = sha1[16 + j];
		for (i = 4*j; i < 4*j+4; i++) {
			c = c + pd->digest[i];
			pd->digest[i] = (uint8_t)c;
			c = c / 256;
		}
	}
}

const char *ek5_hexstr(const ek5_digest_t *pd)
{
#undef	BUFNUM
#undef	BUFSIZE
#define	BUFNUM	32
#define	BUFSIZE	(2*EK5SIZE+1)
	static char *buff[BUFNUM] = {
		NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
		NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
		NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
		NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL
	};
	static int which = 0;

	which = (which + 1) % BUFNUM;
	xfree(buff[which]);
	buff[which] = xcalloc(BUFSIZE, sizeof(char));
	strcpy(buff[which], hexstr(pd->digest, EK5SIZE));

	return buff[which];
}

const char *ek5_hexshort(const ek5_digest_t *pd)
{
#undef	BUFNUM
#undef	BUFSIZE
#define	BUFNUM	32
#define	BUFSIZE	(2*2+3+1)
	static char *buff[BUFNUM] = {
		NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
		NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
		NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
		NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL
	};
	static int which = 0;

	which = (which + 1) % BUFNUM;
	xfree(buff[which]);
	buff[which] = xcalloc(BUFSIZE, sizeof(char));
	pm_snprintf(buff[which], BUFSIZE, "%s...", hexstr(pd->digest, 2));

	return buff[which];
}

int ek5_strhex(ek5_digest_t *pd, const char *src)
{
	size_t i;

	for (i = 0; i < sizeof(pd->digest); i++) {
		if (*src == '\0') return -1;
		pd->digest[i] = 16 * xdigit(*src++);
		if (*src == '\0') return -1;
		pd->digest[i] |= xdigit(*src++);
	}
	if (*src != '\0') return -1;
	return 0;
}

#ifdef	EK5_TEST
configuration_t *g_conf = NULL;
#define	SIZE 4096
int main(int argc, char **argv)
{
	unsigned char buff[SIZE];
	FILE *fp;
	ek5_state_t es;
	ek5_digest_t ek5;

	if (argc > 1) {
		fp = fopen(argv[1], "rb");
		if (NULL == fp) {
			perror(argv[1]);
			exit(1);
		}
	} else {
		fp = stdin;
	}

	ek5_init(&es);
	while (!feof(fp)) {
		size_t done;
		done = fread(buff, 1, SIZE, fp);
		if (done <= 0)
			break;
		ek5_append(&es, buff, done);
	}
	ek5_finish(&es, &ek5);

	if (argc > 1) {
		fclose(fp);
		printf("%s: %s\n", ek5_hexstr(&ek5), argv[1]);
	} else {
		printf("%s\n", ek5_hexstr(&ek5));
	}

	return 0;
}
#endif
